package org.zeapp.opencloud.ui.preferences;

import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;

import org.zeapp.opencloud.framework.chunking.ChunkingDriver;
import org.zeapp.opencloud.framework.chunking.ChunkingDriverManager;
import org.zeapp.opencloud.framework.cloud.Cloud;
import org.zeapp.opencloud.framework.cloud.CloudDriver;
import org.zeapp.opencloud.framework.cloud.CloudDriverManager;
import org.zeapp.opencloud.framework.crypto.AESCrypt;
import org.zeapp.opencloud.framework.preference.CallbackHandler;
import org.zeapp.opencloud.framework.preference.PropertiesPreference;
import org.zeapp.opencloud.framework.synchro.FolderAlias;
import org.zeapp.opencloud.framework.synchro.FolderAliasUtil;
import org.zeapp.opencloud.ui.exception.ExceptionDialog;

import com.jgoodies.forms.factories.FormFactory;
import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;

public class PreferencesDialog extends JDialog {

	private static final long serialVersionUID = 1L;

	private final JPanel contentPanel = new JPanel();
	private JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);

	private JPanel pnContentChunk = new JPanel();
	private JComboBox cbChunkingDriver = new JComboBox();
	private List<ChunkingDriver> chunkingDrivers;

	private JPanel pnContentCloud = new JPanel();
	private JComboBox cbCloudDriver = new JComboBox();
	private List<CloudDriver> cloudDrivers;

	private HashMap<String, PropertiesPreference> preferences = new HashMap<String, PropertiesPreference>();
	private JTable tblFolderAliases;
	private FolderAliasTableModel folderAliasTableModel = new FolderAliasTableModel();

	private PropertiesPreference applicationPreference;
	private Cloud currentCloudService;
	private JTextField txtCryptPassword;
	private String memCryptPassword;

	private AESCrypt cryptEngine;

	/**
	 * Create the dialog.
	 */
	public PreferencesDialog() {
		setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
		setTitle(Messages.getString("PreferencesDialog.this.title")); //$NON-NLS-1$
		setBounds(100, 100, 684, 446);
		getContentPane().setLayout(new BorderLayout());
		contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
		getContentPane().add(contentPanel, BorderLayout.CENTER);
		contentPanel.setLayout(new BorderLayout(0, 0));
		{

			contentPanel.add(tabbedPane, BorderLayout.CENTER);
			{
				JPanel panel = new JPanel();
				tabbedPane
						.addTab("",
								new ImageIcon(
										PreferencesDialog.class
												.getResource("/org/zeapp/opencloud/ui/preferences/pref-general.tab.png")),
								panel, null);
				panel.setLayout(new FormLayout(new ColumnSpec[] {
						FormFactory.RELATED_GAP_COLSPEC,
						FormFactory.DEFAULT_COLSPEC,
						FormFactory.RELATED_GAP_COLSPEC,
						ColumnSpec.decode("default:grow"), }, new RowSpec[] {
						FormFactory.RELATED_GAP_ROWSPEC,
						FormFactory.DEFAULT_ROWSPEC, }));
				{
					JLabel lblCryptoPassword = new JLabel(
							Messages.getString("PreferencesDialog.lblCryptoPassword.text")); //$NON-NLS-1$
					panel.add(lblCryptoPassword, "2, 2, right, default");
				}
				{
					txtCryptPassword = new JTextField();
					txtCryptPassword.addFocusListener(new FocusAdapter() {
						@Override
						public void focusLost(FocusEvent arg0) {
							if (!txtCryptPassword.getText().equals(
									memCryptPassword)) {
								cryptEngine = null;
								try {
									cryptEngine = new AESCrypt(txtCryptPassword
											.getText());
								} catch (GeneralSecurityException e) {
									e.printStackTrace();
								} catch (UnsupportedEncodingException e) {
									e.printStackTrace();
								}

								cloudDriverSelected();
							}
						}

						@Override
						public void focusGained(FocusEvent e) {
							memCryptPassword = txtCryptPassword.getText();
						}
					});
					panel.add(txtCryptPassword, "4, 2, fill, default");
					txtCryptPassword.setColumns(10);
				}
			}
			{
				JPanel panel = new JPanel();
				panel.setBorder(new EmptyBorder(5, 5, 5, 5));
				tabbedPane
						.addTab("",
								new ImageIcon(
										PreferencesDialog.class
												.getResource("/org/zeapp/opencloud/ui/preferences/pref-chunk-tab.png")),
								panel, null);
				panel.setLayout(new BorderLayout(0, 5));
				{
					JPanel panel_1 = new JPanel();
					panel.add(panel_1, BorderLayout.NORTH);
					panel_1.setLayout(new BorderLayout(0, 0));
					{
						JLabel lblChunkingDirver = new JLabel(
								Messages.getString("PreferencesDialog.lblChunkingDirver.text")); //$NON-NLS-1$
						panel_1.add(lblChunkingDirver, BorderLayout.WEST);
					}
					{

						panel_1.add(cbChunkingDriver, BorderLayout.CENTER);
					}
				}
				{
					panel.add(pnContentChunk, BorderLayout.CENTER);
					pnContentChunk.setLayout(new BorderLayout(0, 0));
				}
				tabbedPane.setEnabledAt(1, true);
			}
			{
				JPanel panel = new JPanel();
				panel.setBorder(new EmptyBorder(5, 5, 5, 5));
				tabbedPane
						.addTab("",
								new ImageIcon(
										PreferencesDialog.class
												.getResource("/org/zeapp/opencloud/ui/preferences/pref-cloud-tab.png")),
								panel, null);
				panel.setLayout(new BorderLayout(0, 5));
				{
					JPanel panel_1 = new JPanel();
					panel.add(panel_1, BorderLayout.NORTH);
					panel_1.setLayout(new BorderLayout(0, 0));
					{
						JLabel lblCloudDriver = new JLabel(
								Messages.getString("PreferencesDialog.lblCloudDriver.text")); //$NON-NLS-1$
						panel_1.add(lblCloudDriver, BorderLayout.WEST);
					}
					{

						panel_1.add(cbCloudDriver, BorderLayout.CENTER);
					}
				}
				{
					panel.add(pnContentCloud, BorderLayout.CENTER);
					pnContentCloud.setLayout(new BorderLayout(0, 0));
				}
			}
			{
				JPanel panel = new JPanel();
				panel.setBorder(new EmptyBorder(5, 5, 5, 5));
				tabbedPane
						.addTab("",
								new ImageIcon(
										PreferencesDialog.class
												.getResource("/org/zeapp/opencloud/ui/preferences/pref-alias-tab.png")),
								panel, null);
				panel.setLayout(new BorderLayout(0, 0));
				{
					JPanel panel_1 = new JPanel();
					panel.add(panel_1, BorderLayout.NORTH);
					panel_1.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
					{
						JButton btAddFolderAlias = new JButton(
								Messages.getString("PreferencesDialog.btnNewButton.text")); //$NON-NLS-1$
						btAddFolderAlias
								.addActionListener(new ActionListener() {
									@Override
									public void actionPerformed(ActionEvent arg0) {
										folderAliasTableModel
												.addRow(new FolderAlias());
									}
								});
						panel_1.add(btAddFolderAlias);
					}
				}
				{
					JScrollPane scrollPane = new JScrollPane();
					panel.add(scrollPane, BorderLayout.CENTER);
					{
						tblFolderAliases = new JTable();
						scrollPane.setViewportView(tblFolderAliases);
					}
				}
				tabbedPane.setEnabledAt(3, true);
			}
		}
		{
			JPanel buttonPane = new JPanel();
			buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
			getContentPane().add(buttonPane, BorderLayout.SOUTH);
			{
				JButton okButton = new JButton(
						Messages.getString("PreferencesDialog.okButton.text")); //$NON-NLS-1$
				okButton.addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
						if (apply()) {
							PreferencesDialog.this.setVisible(false);
							PreferencesDialog.this.dispose();
						}
					}
				});
				okButton.setMaximumSize(new Dimension(65, 23));
				okButton.setMinimumSize(new Dimension(65, 23));
				okButton.setActionCommand("OK");
				buttonPane.add(okButton);
				getRootPane().setDefaultButton(okButton);
			}
			{
				JButton cancelButton = new JButton(
						Messages.getString("PreferencesDialog.cancelButton.text")); //$NON-NLS-1$
				cancelButton.setActionCommand("Cancel");
				buttonPane.add(cancelButton);
			}
			{
				JButton btnApply = new JButton(
						Messages.getString("PreferencesDialog.btnApply.text")); //$NON-NLS-1$
				btnApply.setMinimumSize(new Dimension(65, 23));
				btnApply.setMaximumSize(new Dimension(65, 23));
				btnApply.addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent arg0) {
						apply();
					}
				});
				buttonPane.add(btnApply);
			}
		}

	}

	public void initContent() {
		tblFolderAliases.setModel(folderAliasTableModel);

		String filename = System.getProperty("user.home")
				+ "/.ZeappOpenCloud/ZeappOpenCloud.properties";
		applicationPreference = new PropertiesPreference(filename);
		txtCryptPassword.setText(applicationPreference.get("crypting.password",
				""));
		if (!txtCryptPassword.equals("")) {
			cryptEngine = null;
			try {
				cryptEngine = new AESCrypt(txtCryptPassword.getText());
			} catch (GeneralSecurityException e) {
				e.printStackTrace();
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		}
		String driverSelected = applicationPreference.get("chunkingDriver", "");
		int driverIndex = -1;
		chunkingDrivers = ChunkingDriverManager.getDrivers();

		for (ChunkingDriver driver : chunkingDrivers) {
			cbChunkingDriver.addItem(driver.getName());
			if (driver.getClass().getName().equals(driverSelected)) {
				driverIndex = cbChunkingDriver.getItemCount() - 1;
			}
		}
		cbChunkingDriver.setSelectedIndex(-1);
		cbChunkingDriver.addItemListener(new ItemListener() {
			@Override
			public void itemStateChanged(ItemEvent arg0) {
				chunkingDriverSelected();
			}
		});
		if (driverIndex != -1) {
			cbChunkingDriver.setSelectedIndex(driverIndex);
		} else {
			cbChunkingDriver.setSelectedIndex(0);
		}

		driverSelected = applicationPreference.get("cloudDriver", "");
		driverIndex = -1;
		cloudDrivers = CloudDriverManager.getDrivers();
		for (CloudDriver driver : cloudDrivers) {
			cbCloudDriver.addItem(driver.getName());
			if (driver.getClass().getName().equals(driverSelected)) {
				driverIndex = cbCloudDriver.getItemCount() - 1;
			}
		}

		cbCloudDriver.setSelectedIndex(-1);
		cbCloudDriver.addItemListener(new ItemListener() {
			@Override
			public void itemStateChanged(ItemEvent arg0) {
				cloudDriverSelected();
			}
		});
		if (driverIndex != -1) {
			cbCloudDriver.setSelectedIndex(driverIndex);
		} else {
			cbCloudDriver.setSelectedIndex(0);
		}

	}

	private void refreshFolderAliasTable() {
		ArrayList<FolderAlias> aliases = new ArrayList<FolderAlias>();
		// retrieve local aliases.
		int nbLocalAlias = applicationPreference.getInt("folderAlias.count", 0);
		for (int i = 1; i <= nbLocalAlias; i++) {
			String uid = applicationPreference.get("folderAlias." + i + ".uid");
			if (uid != null) {
				String name = applicationPreference.get("folderAlias." + i
						+ ".name");
				String localPath = applicationPreference.get("folderAlias." + i
						+ ".localPath");
				aliases.add(new FolderAlias(uid, name, localPath));
			}
		}
		// retrieve remote aliases
		if (currentCloudService != null) {
			int index = cbCloudDriver.getSelectedIndex();
			if (index >= 0) {

				try {
					List<FolderAlias> remoteAliases = FolderAliasUtil
							.getFolderAliases(cryptEngine, currentCloudService);
					// add/update aliases list
					for (FolderAlias remoteAlias : remoteAliases) {
						int iAlias = aliases.indexOf(remoteAlias);
						if (iAlias != -1) {
							// priority to the remote name
							aliases.get(iAlias).setName(remoteAlias.getName());
						} else {
							aliases.add(remoteAlias);
						}
					}
				} catch (Throwable e) {
					ExceptionDialog.show(e);
				}

			}
		}
		// set table model data
		folderAliasTableModel.setFolderAliases(aliases);
	}

	private void chunkingDriverSelected() {
		int index = cbChunkingDriver.getSelectedIndex();
		ChunkingDriver driver = chunkingDrivers.get(index);
		String filename = System.getProperty("user.home") + "/.ZeappOpenCloud/"
				+ driver.getClass().getName() + ".properties";
		PropertiesPreference pref = preferences.get(filename);
		if (pref == null) {
			pref = new PropertiesPreference(filename);
			preferences.put(filename, pref);
		}
		pnContentChunk.removeAll();
		driver.buildPreferenceScreen(pnContentChunk, pref,
				new CallbackHandler() {

					@Override
					public void needRefresh() {
						// TODO Auto-generated method stub

					}

					@Override
					public void exceptionOccurs(Throwable t) {

						ExceptionDialog.show(PreferencesDialog.this, t);

					}
				});

		applicationPreference
				.set("chunkingDriver", driver.getClass().getName());
	}

	private void cloudDriverSelected() {
		int index = cbCloudDriver.getSelectedIndex();
		final CloudDriver driver = cloudDrivers.get(index);
		final String filename = System.getProperty("user.home")
				+ "/.ZeappOpenCloud/" + driver.getClass().getName()
				+ ".properties";
		PropertiesPreference pref = preferences.get(filename);
		if (pref == null) {
			pref = new PropertiesPreference(filename);
			preferences.put(filename, pref);
		}
		pnContentCloud.removeAll();
		driver.buildPreferenceScreen(pnContentCloud, pref,
				new CallbackHandler() {

					@Override
					public void needRefresh() {
						PropertiesPreference pref = preferences.get(filename);
						driver.applyPreferenceScreenUpdate(pref);
						try {
							currentCloudService = driver.getCloud(pref);
						} catch (Throwable e) {
							currentCloudService = null;
						}
						refreshFolderAliasTable();
					}

					@Override
					public void exceptionOccurs(Throwable t) {

						ExceptionDialog.show(PreferencesDialog.this, t);
					}
				});

		applicationPreference.set("cloudDriver", driver.getClass().getName());

	}

	private boolean apply() {
		if ("".equals(txtCryptPassword.getText())) {
			tabbedPane.setSelectedIndex(0);
			JOptionPane.showMessageDialog(this,
					Messages.getString("Error.CryptingPassword.missing"),
					Messages.getString("Error.Information.missing"),
					JOptionPane.ERROR_MESSAGE);
			return false;
		}
		setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		boolean success = true;
		int index = cbChunkingDriver.getSelectedIndex();
		if (index != -1) {
			ChunkingDriver driver = chunkingDrivers.get(index);
			String filename = System.getProperty("user.home")
					+ "/.ZeappOpenCloud/" + driver.getClass().getName()
					+ ".properties";
			PropertiesPreference pref = preferences.get(filename);

			success = success && driver.applyPreferenceScreenUpdate(pref);
		}

		index = cbCloudDriver.getSelectedIndex();
		if (index != -1) {
			CloudDriver driver = cloudDrivers.get(index);
			String filename = System.getProperty("user.home")
					+ "/.ZeappOpenCloud/" + driver.getClass().getName()
					+ ".properties";
			PropertiesPreference pref = preferences.get(filename);

			success = success && driver.applyPreferenceScreenUpdate(pref);
		}

		int nbLocalAlias = folderAliasTableModel.getRowCount();
		applicationPreference.set("folderAlias.count", nbLocalAlias);
		for (int i = 1; i <= nbLocalAlias; i++) {
			FolderAlias alias = folderAliasTableModel.getFolderAlias(i - 1);
			applicationPreference.set("folderAlias." + i + ".uid",
					alias.getUid());
			applicationPreference.set("folderAlias." + i + ".name",
					alias.getName());
			applicationPreference.set("folderAlias." + i + ".localPath",
					alias.getLocalPath());
		}
		applicationPreference.set("crypting.password",
				txtCryptPassword.getText());

		success = success && applicationPreference.commit();

		if (currentCloudService != null) {
			try {
				FolderAliasUtil.synchronizeFolderAliases(cryptEngine,
						currentCloudService,
						folderAliasTableModel.getFolderAliases());
			} catch (Throwable e) {
				ExceptionDialog.show(e);
				return false;
			}
		}
		setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		return success;
	}
}
